package common

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"net/url"
	"oa-rest/config"
	"strings"
	"time"

	log "github.com/sirupsen/logrus"

	"github.com/minio/minio-go/v7"
	"github.com/minio/minio-go/v7/pkg/credentials"
)

type minioClient struct {
	Client *minio.Client
	bucket string // 默认bucket
}

var MinioClient *minioClient
var DocumentClient *minioClient         // 文档专用bucket客户端
var MeetingClient *minioClient          // 会议专用bucket客户端
var ShareFileClient *minioClient        // 分享文件专用bucket客户端
var SignatureClient *minioClient        // 电子签章专用bucket客户端
var IncomingClient *minioClient         // 收文专用bucket客户端
var OfficialDocumentClient *minioClient // 公文专用bucket客户端

// newMinioClient 创建MinIO客户端
func newMinioClient(endpoint, accessKey, secretKey, bucket string, useSSL bool) (*minioClient, error) {
	// 初始化MinIO客户端
	Client, err := minio.New(endpoint, &minio.Options{
		Creds:  credentials.NewStaticV4(accessKey, secretKey, ""),
		Secure: useSSL,
	})
	if err != nil {
		return nil, fmt.Errorf("创建MinIO客户端失败: %v", err)
	}

	return &minioClient{
		Client: Client,
		bucket: bucket,
	}, nil
}

// UploadFile 上传文件
func (m *minioClient) UploadFile(objectName, filePath string) error {
	// 上传文件
	info, err := m.Client.FPutObject(context.Background(), m.bucket, objectName, filePath, minio.PutObjectOptions{
		ContentType: "application/octet-stream",
	})
	if err != nil {
		return fmt.Errorf("上传文件失败: %v", err)
	}

	fmt.Printf("文件上传成功: %s (大小: %d bytes)\n", objectName, info.Size)
	return nil
}

// UploadFile 上传文件
func (m *minioClient) DeleteBucket(bucketName string) error {
	ctx := context.Background()

	// 1. 检查Bucket是否存在
	exists, err := m.Client.BucketExists(ctx, bucketName)
	if err != nil {
		return fmt.Errorf("检查Bucket存在失败: %v", err)
	}
	if !exists {
		return fmt.Errorf("Bucket '%s' 不存在", bucketName)
	}

	// 2. 删除所有对象
	err = deleteAllObjects(m.Client, bucketName)
	if err != nil {
		return fmt.Errorf("删除对象失败: %v", err)
	}

	// 3. 删除所有未完成的分段上传
	err = deleteAllIncompleteUploads(m.Client, bucketName)
	if err != nil {
		return fmt.Errorf("删除未完成上传失败: %v", err)
	}

	// 4. 删除Bucket
	err = m.Client.RemoveBucket(ctx, bucketName)
	if err != nil {
		return fmt.Errorf("删除Bucket失败: %v", err)
	}

	return nil
}
func deleteAllObjects(client *minio.Client, bucketName string) error {
	ctx := context.Background()

	// 创建对象通道
	objectCh := client.ListObjects(ctx, bucketName, minio.ListObjectsOptions{
		Recursive: true,
	})

	// 批量删除错误通道
	errorCh := client.RemoveObjects(ctx, bucketName, objectCh, minio.RemoveObjectsOptions{})

	// 检查删除错误
	for e := range errorCh {
		return fmt.Errorf("删除对象失败: %v", e.Err)
	}

	return nil
}

func deleteAllIncompleteUploads(client *minio.Client, bucketName string) error {
	ctx := context.Background()

	// 列出所有未完成的上传
	uploadCh := client.ListIncompleteUploads(ctx, bucketName, "", true)

	// 删除每个未完成的上传
	for upload := range uploadCh {
		if upload.Err != nil {
			return fmt.Errorf("列出未完成上传失败: %v", upload.Err)
		}

		err := client.RemoveIncompleteUpload(ctx, bucketName, upload.Key)
		if err != nil {
			return fmt.Errorf("删除未完成上传失败: %v", err)
		}
	}

	return nil
}

// 上传时设置正确的Content-Type
func (m *minioClient) UploadAudioFile(objectName, filePath string) error {
	_, err := m.Client.FPutObject(context.Background(), m.bucket, objectName, filePath, minio.PutObjectOptions{
		ContentType: "audio/mpeg", // 关键：设置正确的Content-Type
	})
	if err != nil {
		return fmt.Errorf("上传音频文件失败: %v", err)
	}

	fmt.Printf("音频文件上传成功: %s\n", objectName)
	return nil
}

// UploadBytesFromMemory 将内存中的字节数据上传到 MinIO
func (m *minioClient) UploadBytesFromMemory(objectName string, data []byte, contentType string) (string, error) {
	reader := bytes.NewReader(data)
	objectSize := int64(len(data))

	info, err := m.Client.PutObject(context.Background(), m.bucket, objectName, reader, objectSize, minio.PutObjectOptions{
		ContentType: contentType,
	})
	if err != nil {
		return "", fmt.Errorf("failed to upload object: %w", err)
	}

	log.Printf("Successfully uploaded %s to %s, ETag: %s\n", objectName, m.bucket, info.ETag)
	// 根据你的 MinIO 配置，返回文件 URL
	return fmt.Sprintf("http://%s/%s/%s", m.Client.EndpointURL().Host, m.bucket, objectName), nil
}

// DownloadFile 下载文件
func (m *minioClient) DownloadFile(objectName, filePath string) error {
	// 下载文件
	err := m.Client.FGetObject(context.Background(), m.bucket, objectName, filePath, minio.GetObjectOptions{})
	if err != nil {
		return fmt.Errorf("下载文件失败: %v", err)
	}

	fmt.Printf("文件下载成功: %s -> %s\n", objectName, filePath)
	return nil
}

// DownloadFileBytes 从 S3 URL 下载文件并返回其字节数据
func (m *minioClient) DownloadFileBytes(s3URL string) ([]byte, error) {
	parsedURL, err := url.Parse(s3URL)
	if err != nil {
		return nil, fmt.Errorf("failed to parse S3 URL: %w", err)
	}

	// 从 URL 路径中提取 Bucket 和 Object Name
	// 假设 URL 格式为 http://endpoint/bucket/object/path
	pathParts := strings.Split(strings.TrimPrefix(parsedURL.Path, "/"), "/")
	if len(pathParts) < 2 {
		return nil, fmt.Errorf("invalid S3 URL format")
	}
	bucketName := pathParts[0]
	objectName := strings.Join(pathParts[1:], "/")

	log.Printf("Starting download from MinIO. Bucket: %s, Object: %s\n", bucketName, objectName)

	// 使用 GetObject 方法下载文件
	object, err := m.Client.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{})
	if err != nil {
		return nil, fmt.Errorf("failed to get object from MinIO: %w", err)
	}
	defer object.Close() // 确保在函数退出时关闭对象

	// 将对象内容读取到字节切片中
	data, err := io.ReadAll(object)
	if err != nil {
		return nil, fmt.Errorf("failed to read object data: %w", err)
	}

	log.Printf("Successfully downloaded %d bytes.\n", len(data))
	return data, nil
}

// ListObjects 列出桶中的对象
func (m *minioClient) ListObjects(prefix string) ([]minio.ObjectInfo, error) {
	var objects []minio.ObjectInfo

	// 列出对象
	objectCh := m.Client.ListObjects(context.Background(), m.bucket, minio.ListObjectsOptions{
		Prefix:    prefix,
		Recursive: true,
	})

	for object := range objectCh {
		if object.Err != nil {
			return nil, fmt.Errorf("列出对象错误: %v", object.Err)
		}
		objects = append(objects, object)
	}

	return objects, nil
}

// GeneratePresignedURL 生成预签名URL
func (m *minioClient) GeneratePresignedURL(objectName string, expiry time.Duration) (string, error) {
	// 生成预签名URL
	url, err := m.Client.PresignedGetObject(context.Background(), m.bucket, objectName, expiry, nil)
	if err != nil {
		return "", fmt.Errorf("生成预签名URL失败: %v", err)
	}

	return url.String(), nil
}

// DeleteObject 删除对象
func (m *minioClient) DeleteObject(objectName string) error {
	err := m.Client.RemoveObject(context.Background(), m.bucket, objectName, minio.RemoveObjectOptions{})
	if err != nil {
		return fmt.Errorf("删除对象失败: %v", err)
	}

	fmt.Printf("对象删除成功: %s\n", objectName)
	return nil
}

// 设置桶为公开读取
func (m *minioClient) SetBucketPublic(bucketName string) error {
	policy := `{
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Principal": {"AWS": ["*"]},
                "Action": ["s3:GetObject"],
                "Resource": ["arn:aws:s3:::` + bucketName + `/*"]
            }
        ]
    }`

	err := m.Client.SetBucketPolicy(context.Background(), bucketName, policy)
	if err != nil {
		return fmt.Errorf("设置桶策略失败: %v", err)
	}

	return nil
}

// 生成简洁的永久URL
func (m *minioClient) GeneratePermanentURL(objectName string) string {
	return fmt.Sprintf("http://%s/%s/%s", m.Client.EndpointURL(), m.bucket, objectName)
}

func InitMinioClient() {
	// 初始化默认客户端（test bucket）
	c, err := newMinioClient(config.MinioEndpoint, config.MinioAccessKey, config.MinioSecretKey, config.MinioBucket, config.MinioUseSSL)
	if err != nil {
		log.Fatalf("init minio client failed: %v", err)
	}
	MinioClient = c

	// 初始化文档专用客户端（documents bucket）
	docClient, err := newMinioClient(config.MinioEndpoint, config.MinioAccessKey, config.MinioSecretKey, "documents", config.MinioUseSSL)
	if err != nil {
		log.Fatalf("init document minio client failed: %v", err)
	}
	DocumentClient = docClient

	// 初始化会议专用客户端（meeting bucket）
	meetingClient, err := newMinioClient(config.MinioEndpoint, config.MinioAccessKey, config.MinioSecretKey, "meeting", config.MinioUseSSL)
	if err != nil {
		log.Fatalf("init meeting minio client failed: %v", err)
	}
	MeetingClient = meetingClient

	// 初始化分享文件专用客户端（sharefile bucket）
	sharefileClient, err := newMinioClient(config.MinioEndpoint, config.MinioAccessKey, config.MinioSecretKey, "sharefile", config.MinioUseSSL)
	if err != nil {
		log.Fatalf("init sharefile minio client failed: %v", err)
	}
	ShareFileClient = sharefileClient

	// 初始化电子签章专用客户端（signature bucket）
	signatureClient, err := newMinioClient(config.MinioEndpoint, config.MinioAccessKey, config.MinioSecretKey, "signature", config.MinioUseSSL)
	if err != nil {
		log.Fatalf("init signature minio client failed: %v", err)
	}
	SignatureClient = signatureClient

	// 初始化收文专用客户端（incoming bucket）
	incomingClient, err := newMinioClient(config.MinioEndpoint, config.MinioAccessKey, config.MinioSecretKey, "incoming", config.MinioUseSSL)
	if err != nil {
		log.Fatalf("init incoming minio client failed: %v", err)
	}
	IncomingClient = incomingClient

	// 初始化公文专用客户端（official-document bucket）
	officialDocClient, err := newMinioClient(config.MinioEndpoint, config.MinioAccessKey, config.MinioSecretKey, "official-document", config.MinioUseSSL)
	if err != nil {
		log.Fatalf("init officialDocument minio client failed: %v", err)
	}
	OfficialDocumentClient = officialDocClient
}

// UploadDocument 上传文档到documents bucket
func UploadDocument(objectName string, data []byte, contentType string) (string, error) {
	if DocumentClient == nil {
		return "", fmt.Errorf("document client not initialized")
	}
	return DocumentClient.UploadBytesFromMemory(objectName, data, contentType)
}

// UploadDocumentFile 上传文档文件到documents bucket
func UploadDocumentFile(objectName, filePath string) error {
	if DocumentClient == nil {
		return fmt.Errorf("document client not initialized")
	}
	return DocumentClient.UploadFile(objectName, filePath)
}

// DownloadDocument 从documents bucket下载文档
func DownloadDocument(objectName string) ([]byte, error) {
	if DocumentClient == nil {
		return nil, fmt.Errorf("document client not initialized")
	}

	object, err := DocumentClient.Client.GetObject(context.Background(), "documents", objectName, minio.GetObjectOptions{})
	if err != nil {
		return nil, fmt.Errorf("failed to get document: %w", err)
	}
	defer object.Close()

	return io.ReadAll(object)
}

// ListDocuments 列出documents bucket中的文档
func ListDocuments(prefix string) ([]minio.ObjectInfo, error) {
	if DocumentClient == nil {
		return nil, fmt.Errorf("document client not initialized")
	}
	return DocumentClient.ListObjects(prefix)
}

// GenerateDocumentURL 生成文档访问URL
func GenerateDocumentURL(objectName string) string {
	if DocumentClient == nil {
		return ""
	}
	return DocumentClient.GeneratePermanentURL(objectName)
}

// UploadMeeting 上传会议文件到meeting bucket
func UploadMeeting(objectName string, data []byte, contentType string) (string, error) {
	if MeetingClient == nil {
		return "", fmt.Errorf("meeting client not initialized")
	}
	return MeetingClient.UploadBytesFromMemory(objectName, data, contentType)
}

// UploadMeetingFile 上传会议文件到meeting bucket
func UploadMeetingFile(objectName, filePath string) error {
	if MeetingClient == nil {
		return fmt.Errorf("meeting client not initialized")
	}
	return MeetingClient.UploadFile(objectName, filePath)
}

// UploadMeetingAudio 上传会议录音到meeting bucket
func UploadMeetingAudio(objectName, filePath string) error {
	if MeetingClient == nil {
		return fmt.Errorf("meeting client not initialized")
	}
	return MeetingClient.UploadAudioFile(objectName, filePath)
}

// UploadSharefile 上传分享文件到sharefile bucket
func UploadSharefile(objectName string, data []byte, contentType string) (string, error) {
	if ShareFileClient == nil {
		return "", fmt.Errorf("sharefile client not initialized")
	}
	return ShareFileClient.UploadBytesFromMemory(objectName, data, contentType)
}

// UploadSharefileFile 上传分享文件到sharefile bucket
func UploadSharefileFile(objectName, filePath string) error {
	if ShareFileClient == nil {
		return fmt.Errorf("sharefile client not initialized")
	}
	return ShareFileClient.UploadFile(objectName, filePath)
}

// UploadSignature 上传电子签章到signature bucket
func UploadSignature(objectName string, data []byte, contentType string) (string, error) {
	if SignatureClient == nil {
		return "", fmt.Errorf("signature client not initialized")
	}
	return SignatureClient.UploadBytesFromMemory(objectName, data, contentType)
}

// UploadSignatureFile 上传电子签章文件到signature bucket
func UploadSignatureFile(objectName, filePath string) error {
	if SignatureClient == nil {
		return fmt.Errorf("signature client not initialized")
	}
	return SignatureClient.UploadFile(objectName, filePath)
}

// GetSignatureURL 获取电子签章访问URL
func GetSignatureURL(objectName string) string {
	if SignatureClient == nil {
		return ""
	}
	return SignatureClient.GeneratePermanentURL(objectName)
}

// DeleteSignature 删除电子签章
func DeleteSignature(objectName string) error {
	if SignatureClient == nil {
		return fmt.Errorf("signature client not initialized")
	}
	return SignatureClient.DeleteObject(objectName)
}

// UploadIncoming 上传收文到incoming bucket
func UploadIncoming(objectName string, data []byte, contentType string) (string, error) {
	if IncomingClient == nil {
		return "", fmt.Errorf("incoming client not initialized")
	}
	return IncomingClient.UploadBytesFromMemory(objectName, data, contentType)
}

// DownloadIncoming 从incoming bucket下载收文
func DownloadIncoming(objectName string) ([]byte, error) {
	if IncomingClient == nil {
		return nil, fmt.Errorf("incoming client not initialized")
	}

	object, err := IncomingClient.Client.GetObject(context.Background(), "incoming", objectName, minio.GetObjectOptions{})
	if err != nil {
		return nil, fmt.Errorf("failed to get incoming file: %w", err)
	}
	defer object.Close()

	return io.ReadAll(object)
}

// GenerateIncomingURL 生成收文访问URL
func GenerateIncomingURL(objectName string) string {
	if IncomingClient == nil {
		return ""
	}
	return IncomingClient.GeneratePermanentURL(objectName)
}

// UploadOfficialDocument 上传公文到officialDocument bucket
func UploadOfficialDocument(objectName string, data []byte, contentType string) (string, error) {
	if OfficialDocumentClient == nil {
		return "", fmt.Errorf("official document client not initialized")
	}
	return OfficialDocumentClient.UploadBytesFromMemory(objectName, data, contentType)
}

// UploadOfficialDocumentFile 上传公文文件到officialDocument bucket
func UploadOfficialDocumentFile(objectName, filePath string) error {
	if OfficialDocumentClient == nil {
		return fmt.Errorf("official document client not initialized")
	}
	return OfficialDocumentClient.UploadFile(objectName, filePath)
}

// GenerateOfficialDocumentURL 生成公文访问URL
func GenerateOfficialDocumentURL(objectName string) string {
	if OfficialDocumentClient == nil {
		return ""
	}
	return OfficialDocumentClient.GeneratePermanentURL(objectName)
}

// DownloadMeeting 从meeting bucket下载会议文件
func DownloadMeeting(objectName string) ([]byte, error) {
	if MeetingClient == nil {
		return nil, fmt.Errorf("meeting client not initialized")
	}

	object, err := MeetingClient.Client.GetObject(context.Background(), "meeting", objectName, minio.GetObjectOptions{})
	if err != nil {
		return nil, fmt.Errorf("failed to get meeting file: %w", err)
	}
	defer object.Close()

	return io.ReadAll(object)
}

// ListMeetings 列出meeting bucket中的会议文件
func ListMeetings(prefix string) ([]minio.ObjectInfo, error) {
	if MeetingClient == nil {
		return nil, fmt.Errorf("meeting client not initialized")
	}
	return MeetingClient.ListObjects(prefix)
}

// GenerateMeetingURL 生成会议文件访问URL
func GenerateMeetingURL(objectName string) string {
	if MeetingClient == nil {
		return ""
	}
	return MeetingClient.GeneratePermanentURL(objectName)
}

// CreateBucket 创建新的bucket
func CreateBucket(bucketName string, public bool) error {
	err := MinioClient.Client.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{})
	if err != nil {
		return err
	}

	// 如果需要设置为公开访问
	if public {
		err = MinioClient.SetBucketPublic(bucketName)
		if err != nil {
			return fmt.Errorf("设置bucket: %s为公开访问失败: %v", bucketName, err)
		}
	}

	return nil
}

// UploadToDynamicBucket 上传文件到指定的动态bucket
func UploadToDynamicBucket(bucketName string, objectName string, data []byte, contentType string) (string, error) {
	// 创建临时客户端用于动态bucket上传
	tempClient, err := newMinioClient(config.MinioEndpoint, config.MinioAccessKey, config.MinioSecretKey, bucketName, config.MinioUseSSL)
	if err != nil {
		return "", fmt.Errorf("failed to create temp client for bucket %s: %w", bucketName, err)
	}

	// 确保bucket存在，如果不存在则创建
	exists, err := tempClient.Client.BucketExists(context.Background(), bucketName)
	if err != nil {
		return "", fmt.Errorf("failed to check bucket existence: %w", err)
	}
	if !exists {
		err = tempClient.Client.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{})
		if err != nil {
			return "", fmt.Errorf("failed to create bucket %s: %w", bucketName, err)
		}
		log.Printf("Created new bucket: %s\n", bucketName)
	}

	// 直接使用MinIO客户端上传，指定正确的bucket名称
	reader := bytes.NewReader(data)
	objectSize := int64(len(data))

	info, err := tempClient.Client.PutObject(context.Background(), bucketName, objectName, reader, objectSize, minio.PutObjectOptions{
		ContentType: contentType,
	})
	if err != nil {
		return "", fmt.Errorf("failed to upload object to bucket %s: %w", bucketName, err)
	}

	log.Printf("Successfully uploaded %s to bucket %s, ETag: %s\n", objectName, bucketName, info.ETag)

	// 根据MinIO配置，返回文件URL
	return fmt.Sprintf("http://%s/%s/%s", tempClient.Client.EndpointURL().Host, bucketName, objectName), nil
}
